-
Notifications
You must be signed in to change notification settings - Fork 125
Allow immediate request failure on connection error #625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow immediate request failure on connection error #625
Conversation
Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1StateTests.swift
Outdated
Show resolved
Hide resolved
`AsyncAwaitEndToEndTests.testImmediateDeadline` failed in CI. Hard to debug without more information. Doesn’t fail locally with 1000 repetitions.
Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift
Show resolved
Hide resolved
Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift
Outdated
Show resolved
Hide resolved
guard self.retryConnectionEstablishment else { | ||
return .init( | ||
request: self.failAllRequests(reason: error), | ||
connection: .none | ||
) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is weird! If we always fail all requests, when we a connection attempt failed, we should never reschedule a connection attempt. And this means we should never reach this point. Since we never schedule a backoff timer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
waitingForConnectivity
is called if a NWConnection
transitions into the waiting state which has nothing to do with our own connection retry logic. This means that it will be called if a NWConnection "fails" instead of failedToCreateNewConnection
because the connection will do its own retry thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please add a code comment, that this is only triggered in the Network.framework
flow?
If we don't want to retry connection establishment, NW.framework should not go into the waiting state, should it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed the code again. We have a different flag (networkFrameworkWaitForConnectivity
) for disabling Network.frameworks waiting behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And a bit more...
Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift
Show resolved
Hide resolved
Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift
Outdated
Show resolved
Hide resolved
Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift
Outdated
Show resolved
Hide resolved
@@ -219,6 +223,17 @@ extension HTTPConnectionPool { | |||
|
|||
switch self.lifecycleState { | |||
case .running: | |||
guard self.retryConnectionEstablishment else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using the guard here it might be nicer to go with where clause in the switch...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A where constraint only applies to the last case if multiple cases are defined e.g.
case .running, .shuttingDown where self.retryConnectionEstablishment == true:
the where constrain only applies to .shuttingDown
but not .running
. We had an issue in AHC which we debugged together and decided we should stop using where clauses in switch cases altogether because it is not obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, however here we only have one case here. Anyway, not a hill I want to die on. If we want to stick with an inline if/guard, I would prefer this to be an if self.retryConnectionEstablishment {}
and then move the retry logic into the if.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit of the if
? Guard at least guarantees that we return, if can fall through which we don't want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I consider it nicer to read. But that's personal...
@@ -401,13 +405,38 @@ extension HTTPConnectionPool { | |||
self.failedConsecutiveConnectionAttempts += 1 | |||
self.lastConnectFailure = error | |||
|
|||
guard self.retryConnectionEstablishment else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we check the current running state here? Like in the http1 case? This seems weird. I think we should add this here. If the pool is closing, we should not backoff anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah agree this is weird but has nothing to do with this change as we don't check the state at all. We should tackle this in a separate PR as we likely want to add test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need TODOs for the incorrect http2 shutdown behavior, and we should change handling in waiting for NW.framework and/or document it better.
@@ -219,6 +223,17 @@ extension HTTPConnectionPool { | |||
|
|||
switch self.lifecycleState { | |||
case .running: | |||
guard self.retryConnectionEstablishment else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, however here we only have one case here. Anyway, not a hill I want to die on. If we want to stick with an inline if/guard, I would prefer this to be an if self.retryConnectionEstablishment {}
and then move the retry logic into the if.
guard self.retryConnectionEstablishment else { | ||
return .init( | ||
request: self.failAllRequests(reason: error), | ||
connection: .none | ||
) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please add a code comment, that this is only triggered in the Network.framework
flow?
If we don't want to retry connection establishment, NW.framework should not go into the waiting state, should it?
Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pushing through!
Motivation
In integration tests we often want to inspect the connection error thrown during connection establishment for a request. Currently the only way to get at the connection error is to wait until the request timeout is expired. This is because of the connection retry behaviour backed into AHC which cannot be turned off. AHC will try to create new connections on connection failure with an exponential backoff. It stores the last connection and will fail the request if the request times out.
Modification
Adds a new internal flag called
retryConnectionEstablishment
toHTTPClient.Configuration.ConnectionPool
.If false, requests will fail immediately after a connection could not be established. Otherwise the old behavior is used which is still the default.
Set
retryConnectionEstablishment
to false to speedup test from ~40s to ~1s inHTTPClientNIOTSTests.swift
. More tests will follow.Result
Allows fast and reliable test execution.